package com.liuyi.usercenterservice.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.generator.config.IFileCreate;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.liuyi.usercenterservice.common.Commons;
import com.liuyi.usercenterservice.common.ErrorCode;
import com.liuyi.usercenterservice.common.RedisKey;
import com.liuyi.usercenterservice.entity.domain.User;
import com.liuyi.usercenterservice.entity.vo.UserVO;
import com.liuyi.usercenterservice.exception.BusinessException;
import com.liuyi.usercenterservice.mapper.UserMapper;
import com.liuyi.usercenterservice.service.UserService;
import com.liuyi.usercenterservice.tools.MatchUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author liuyi
 * @since 2024-03-16
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    public static final String STATE = "liuyi";
    public static final String USER_STATUS = "userStatus";
    @Resource
    RedisTemplate<String , Object> redisTemplate;
    @Resource
    UserMapper userMapper;

    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword) {

        //region Description 校验
        if (StringUtils.isAnyBlank(userAccount, userAccount, checkPassword)) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        if (userAccount.length() < Commons.USER_ACCOUNT_LENGTH || userPassword.length() < Commons.USER_PASSWORD_LENGTH
                || checkPassword.length() < Commons.USER_PASSWORD_LENGTH) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数长度不符合");
        }
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "确认密码与密码不一致");
        }
        //特殊字符判断
        String regEx = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]";
        Pattern compile = Pattern.compile(regEx);
        if (compile.matcher(userAccount).find()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号存在特殊字符");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_account", userAccount);
        if (userMapper.selectOne(queryWrapper) != null) {
            throw new BusinessException(ErrorCode.EXIST_DATA, "该用户已存在");
        }
        //endregion

        //密码加密
        String newPassword = DigestUtils.md5DigestAsHex((STATE + userPassword).getBytes());

        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(newPassword);
        user.setCreateTime(new Date());
        int result = userMapper.insert(user);
        return result > 0 ? user.getId() : -1;
    }

    @Override
    public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        //1.校验
        //region 校验
        if (StringUtils.isAnyBlank(userAccount, userAccount)) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        if (userAccount.length() < Commons.USER_ACCOUNT_LENGTH || userPassword.length() < Commons.USER_PASSWORD_LENGTH) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数长度不符合");
        }
        //endregion
        //2.查询是否存在用户且密码正确
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        String checkPassword = DigestUtils.md5DigestAsHex((STATE + userPassword).getBytes());
        queryWrapper.eq("user_account", userAccount).eq("user_password", checkPassword);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_EXIST_DATA, "用户不存在");
        }
        //记录用户登入时间
        user.setLoginTime(new Date());
        userMapper.updateById(user);
        //3.用户信息脱敏
        User safeUser = getSafeUser(user);
        //4.给 session 记录登录状态
        request.getSession().setAttribute(USER_STATUS, safeUser);
        return safeUser;
    }


    @Override
    public User getSafeUser(User user) {
        if (user == null) {
            return null;
        }
        User safeUser = new User();
        safeUser.setId(user.getId());
        safeUser.setUsername(user.getUsername());
        safeUser.setUserAccount(user.getUserAccount());
        safeUser.setGender(user.getGender());
        safeUser.setPhone(user.getPhone());
        safeUser.setEmail(user.getEmail());
        safeUser.setAvatarUrl(user.getAvatarUrl());
        safeUser.setTags(user.getTags());
        safeUser.setUserStatus(user.getUserStatus());
        safeUser.setUserRole(user.getUserRole());
        safeUser.setUserRole(user.getUserRole());
        safeUser.setProfile(user.getProfile());
        safeUser.setCreateTime(user.getCreateTime());
        safeUser.setLoginTime(user.getLoginTime());
        return safeUser;
    }

    @Override
    public boolean isAdmin(HttpServletRequest request) {
        User userStatus = (User) request.getSession().getAttribute(USER_STATUS);
        return userStatus != null && userStatus.getUserRole() == 1;
    }

    @Override
    public boolean isAdmin(User loginUser) {
        return loginUser != null && loginUser.getUserRole() == 1;
    }

    @Override
    public List<User> searchUsersByTags(List<String> tags) {
        if (CollectionUtils.isEmpty(tags)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //查询所有用户
        List<User> users = userMapper.selectList(new QueryWrapper<>());
        //匹配过滤
        Gson gson = new Gson();
        List<User> result = users.stream().filter(user -> {
            if (StringUtils.isBlank(user.getTags())) {
                return false;
            }
            Set<String> userTags = gson.fromJson(user.getTags(), new TypeToken<Set<String>>() {
            }.getType());
            for (String tag : tags) {
                if (userTags.contains(tag)) {
                    return true;
                }
            }
            return false;
        }).map(this::getSafeUser).collect(Collectors.toList());
        return result;
    }

    @Override
    public User getLoginUser(HttpServletRequest request) {
        User loginUser = (User) request.getSession().getAttribute(USER_STATUS);
        if (loginUser == null){
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        return loginUser;
    }

    @Override
    public int updateUserById(User user, User loginUser) {
        if (user.getId() <= 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //非管理员不允许修改非本人的信息
        if (!isAdmin(loginUser) && !user.getId().equals(loginUser.getId())){
            throw new BusinessException(ErrorCode.NO_PERMISSION);
        }
        User selectUser = userMapper.selectById(user.getId());
        if (selectUser == null){
            throw new BusinessException(ErrorCode.NOT_EXIST_DATA);
        }
        user.setUpdateTime(new Date());
        return userMapper.updateById(user);
    }

    @Override
    public List<User> recomendUser(User loginUser,Page page) {
        if (loginUser == null){
            return null;
        }
        ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
        String key = RedisKey.RECOMMEND_KEY + loginUser.getId();
        long current = page.getCurrent();
        if (current <= 1L){
            //Redis中有缓存则在Redis中取
            List<User> users = (List<User>) opsForValue.get(key);
            if (!CollectionUtils.isEmpty(users)){
                return users;
            }
        }
        //Redis中无缓存这在数据库中取
        String tags = loginUser.getTags();
        Page<User> userPage = getUsersByTags(tags, page,loginUser);
        List<User> records = userPage.getRecords();
        if (current <= 1L){
            //存入缓存中 并设置过期时间
            opsForValue.set(key,records,3, TimeUnit.HOURS);
        }
        return records;
    }
    @Override
    public Page<User> getUsersByTags(String tags, Page<User> page,User loginUser) {
        Gson gson = new Gson();
        Set<String> tagSet = gson.fromJson(tags, new TypeToken<Set<String>>() {
        }.getType());
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.ne("id",loginUser.getId())
                .and(q -> {
                    for (String tag : tagSet) {
                        q.or().like("tags","%" + tag + "%");
                    }
                });

        return userMapper.selectPage(page, queryWrapper);
    }

    @Override
    public List<User> matchUser(User loginUser,Page<User> page) {
        if (ObjectUtils.isEmpty(loginUser)){
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        String tagsStr = loginUser.getTags();
        //根据标签获取用户
        List<User> users = getUsersByTags(tagsStr,page,loginUser).getRecords();
        Gson gson = new Gson();
        List<String> userTagsList = gson.fromJson(tagsStr, new TypeToken<List<String>>() {}.getType());
        //维护匹配的数据
        PriorityQueue<Map.Entry<Integer, User>> queue = new PriorityQueue<>(Commons.DEFAULT_MATCH_NUM, Comparator.comparingInt(Map.Entry::getKey));

        for (User user : users) {
            List<String> tags = gson.fromJson(user.getTags(), new TypeToken<List<String>>() {}.getType());
            int distance = MatchUtils.minDistance(tags, userTagsList);
            if (queue.size() < Commons.DEFAULT_MATCH_NUM){
                queue.add(new AbstractMap.SimpleEntry<>(distance, user));
            }else {
                Map.Entry<Integer, User> peek = queue.peek();
                assert peek != null;
                if (peek.getKey() > distance){
                    queue.remove(peek);
                    queue.add(new AbstractMap.SimpleEntry<>(distance, user));
                }
            }
        }
        return queue.stream().map(e -> this.getSafeUser(e.getValue())).collect(Collectors.toList());
    }
}
